#ifndef __XGU_DELEGATE02_H__
#define __XGU_DELEGATE02_H__

#include "Functor.h"
#include "MemberFunctor.h"

#include <vector>
using std::vector;

#include <cassert>

namespace xgu
{
	// -----------------------------------------------------------
	// 2 Arguments
	template <typename TRet, typename TArg0, typename TArg1>
	class Delegate<TRet, TArg0, TArg1>
	{
	public:
		typedef vector<TRet> ReturnType;

		Delegate() {}
		virtual ~Delegate()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
		}

		Delegate(const Delegate<TRet, TArg0, TArg1>& delegate)
		{
			Add(delegate);
		}

		Delegate& operator=(const Delegate<TRet>& delegate)
		{
			RemoveAll();
			Add(delegate);
			return *this;
		}

		template <typename TClass>
		Delegate(TClass& C, TRet (TClass::*F)(TArg0, TArg1))
		{
			Add<TClass>(C, F);
		}
		template <typename TClass>
		Delegate(TClass* pC, TRet (TClass::*F)(TArg0, TArg1))
		{
			Add<TClass>(pC, F);
		}

		Delegate& operator += (const Delegate<TRet, TArg0, TArg1>& Value)
		{
			Add(Value);
			return *this;
		}
		Delegate& operator -= (const Delegate<TRet, TArg0, TArg1>& Value)
		{
			Remove(Value);
			return *this;
		}

		virtual vector<TRet> operator() (TArg0 a0, TArg1 a1) const
		{
			vector<TRet> ret;
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				Functor<TRet, TArg0, TArg1>* f = delegates[i];
				assert(f);
				ret.push_back((*f)(a0, a1));
			}
			return ret;
		}


		template<typename TClass>
		void Add(TClass& C, TRet (TClass::*F)(TArg0, TArg1))
		{
			Functor<TRet, TArg0, TArg1>* f = new MemberFunctor<TClass, TRet, TArg0, TArg1>(C, F);
			assert(f);
			delegates.push_back(f);
		}
		template<typename TClass>
		void Add(TClass* pC, TRet (TClass::*F)(TArg0, TArg1))
		{
			assert(pC);
			Functor<TRet, TArg0, TArg1>* f = new MemberFunctor<TClass, TRet, TArg0, TArg1>(*pC, F);
			assert(f);
			delegates.push_back(f);
		}
		void Add(const Delegate<TRet, TArg0, TArg1>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Add(D.delegates[i]);
			}
		}
		void Remove(const Delegate<TRet, TArg0, TArg1>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Functor<TRet, TArg0, TArg1>* f = D.delegates[i];
				assert(f);
				Remove(f);
			}
		}

		void RemoveAll()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
			delegates.clear();
		}

	protected:

		void Add(const Functor<TRet, TArg0, TArg1>* F)
		{
			assert(F);
			delegates.push_back(F->Clone());
		}

		void Remove(const Functor<TRet, TArg0, TArg1>* F)
		{
			assert(F);
			for (vector<Functor<TRet, TArg0, TArg1>*>::const_iterator iter = delegates.begin();
				iter != delegates.end(); ++iter)
			{
				const Functor<TRet, TArg0, TArg1>* f = *iter;
				assert(f);
				if (*f == *F)
				{
					delete *iter;
					delegates.erase(iter);
					break;
				}
			}
		}

	private:
		vector<Functor<TRet, TArg0, TArg1>*> delegates;
	};


	
	// ------------------------------------------------------------------
	// Void Return Type Specialization
	template <typename TArg0, typename TArg1>
	class Delegate<void, TArg0, TArg1>
	{
		typedef void TRet;

	public:

		typedef void ReturnType;

		Delegate() {}
		virtual ~Delegate()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
		}

		Delegate(const Delegate<TRet, TArg0, TArg1>& delegate)
		{
			Add(delegate);
		}

		Delegate& operator=(const Delegate<TRet>& delegate)
		{
			RemoveAll();
			Add(delegate);
			return *this;
		}

		template <typename TClass>
		Delegate(TClass& C, TRet (TClass::*F)(TArg0, TArg1))
		{
			Add<TClass>(C, F);
		}
		template <typename TClass>
		Delegate(TClass* pC, TRet (TClass::*F)(TArg0, TArg1))
		{
			Add<TClass>(pC, F);
		}

		Delegate& operator += (const Delegate<TRet, TArg0, TArg1>& Value)
		{
			Add(Value);
			return *this;
		}
		Delegate& operator -= (const Delegate<TRet, TArg0, TArg1>& Value)
		{
			Remove(Value);
			return *this;
		}

		virtual void operator() (TArg0 a0, TArg1 a1) const
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				Functor<TRet, TArg0, TArg1>* f = delegates[i];
				assert(f);
				(*f)(a0, a1);
			}
		}


		template<typename TClass>
		void Add(TClass& C, TRet (TClass::*F)(TArg0, TArg1))
		{
			Functor<TRet, TArg0, TArg1>* f = new MemberFunctor<TClass, TRet, TArg0, TArg1>(C, F);
			assert(f);
			delegates.push_back(f);
		}
		template<typename TClass>
		void Add(TClass* pC, TRet (TClass::*F)(TArg0, TArg1))
		{
			assert(pC);
			Functor<TRet, TArg0, TArg1>* f = new MemberFunctor<TClass, TRet, TArg0, TArg1>(*pC, F);
			assert(f);
			delegates.push_back(f);
		}
		void Add(const Delegate<TRet, TArg0, TArg1>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Add(D.delegates[i]);
			}
		}
		void Remove(const Delegate<TRet, TArg0, TArg1>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Functor<TRet, TArg0, TArg1>* f = D.delegates[i];
				assert(f);
				Remove(f);
			}
		}

		void RemoveAll()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
			delegates.clear();
		}

	protected:

		void Add(const Functor<TRet, TArg0, TArg1>* F)
		{
			assert(F);
			delegates.push_back(F->Clone());
		}

		void Remove(const Functor<TRet, TArg0, TArg1>* F)
		{
			assert(F);
			for (vector<Functor<TRet, TArg0, TArg1>*>::const_iterator iter = delegates.begin();
				iter != delegates.end(); ++iter)
			{
				const Functor<TRet, TArg0, TArg1>* f = *iter;
				assert(f);
				if (*f == *F)
				{
					delete *iter;
					delegates.erase(iter);
					break;
				}
			}
		}

	private:
		vector<Functor<TRet, TArg0, TArg1>*> delegates;
	};


}

#endif